// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "stdafx.h"
#include "CFpsParticleEmitter.h"
#include "IAttributes.h"
#include "_Math.h"


array<SFpsParticle> CFpsParticleEmitter::Particles;

//! constructor
CFpsParticleEmitter::CFpsParticleEmitter(
	const rectf& box,
	const vector2df& direction, u32 speedVariation,
	u32 minParticlesPerSecond, u32 maxParticlesPerSecond,
	f32 particleWidth, f32 particleHeight, s32 sizeVariation,
	SColor minStartColor, SColor maxStartColor,
	float lifeTimeMin, float lifeTimeMax,
	s32 maxAngleDegrees,
	s32 initialRotMin, s32 initialRotMax)
 : m_box(box),Direction(direction), SpeedVariation(speedVariation),
	MinParticlesPerSecond(minParticlesPerSecond), MaxParticlesPerSecond(maxParticlesPerSecond),
	ParticleWidth(particleWidth), ParticleHeight(particleHeight), SizeVariation(sizeVariation),
	MinStartColor(minStartColor), MaxStartColor(maxStartColor),
	MinLifeTime(lifeTimeMin), MaxLifeTime(lifeTimeMax), Time(0), Emitted(0),
	MaxAngleDegrees(maxAngleDegrees),
	InitialRotMin(initialRotMin), InitialRotMax(initialRotMax)
{

}



//! Prepares an array with new particles to emitt into the system
//! and returns how much new particles there are.
s32 CFpsParticleEmitter::emitt(float now, float timeSinceLastCall, SFpsParticle*& outArray)
{
	u32 pps = (MaxParticlesPerSecond - MinParticlesPerSecond);
	f32 perSecond = pps ? (f32)MinParticlesPerSecond + (CMath::NextRandom(32) % pps) : MinParticlesPerSecond;
	float everyWhatMillisecond = 1.0f / perSecond;

	/*quaternion qRot;
	qRot.rotationFromTo(Vector3D(0,1,0), Direction);

	Vector3D eulerAngles;
	qRot.toEulerDegree(eulerAngles);*/

	if (Time > everyWhatMillisecond)
	{
		Particles.set_used(0);
		u32 amount = (u32)((Time / everyWhatMillisecond) + 0.5f);

		if (amount > MaxParticlesPerSecond*2)
			amount = MaxParticlesPerSecond * 2;

		Time = 0;
		SFpsParticle p;
		//const vector3df& extent = Box.getExtent();

		for (u32 i=0; i<amount; ++i)
		{
			/*p.pos.X = Box.MinEdge.X + fmodf((f32)CMath::NextRandom(32), extent.X);
			p.pos.Y = Box.MinEdge.Y + fmodf((f32)CMath::NextRandom(32), extent.Y);
			p.pos.Z = Box.MinEdge.Z + fmodf((f32)CMath::NextRandom(32), extent.Z);*/
			p.pos.X = fmodf((f32)CMath::NextRandom(32), m_box.getWidth());
			p.pos.Y = fmodf((f32)CMath::NextRandom(32), m_box.getHeight());


			p.startTime = now;
			//p.vector = Direction;

			p.width = ParticleWidth;
			p.height = ParticleHeight;

			p.spin = 0;

			int deltaRot = InitialRotMax - InitialRotMin;
			/*p.initialRotation = eulerAngles.X + InitialRotMin;

			if (deltaRot)
			{
				p.initialRotation = InitialRotMin + (CMath::NextRandom(32) % (deltaRot));
			}*/

			if (SizeVariation != 0)
			{
				int rnd = CMath::NextRandom(32) % (SizeVariation << 1) - SizeVariation;
				p.width = p.width + p.width * rnd / 100;
				p.height = p.height + p.height * rnd / 100;
			}
			
			/*vector3df tgt = Direction;

			if (MaxAngleDegreesXY)
			{
				tgt.rotateXYBy((CMath::NextRandom(32)%(MaxAngleDegreesXY*2)) - MaxAngleDegreesXY);
			}

			p.vector = tgt;*/

			if (MaxLifeTime - MinLifeTime == 0)
				p.endTime = now + MinLifeTime;
			else
				p.endTime = now +  CMath:: GetGLRand( MinLifeTime, MaxLifeTime );

			p.color = MinStartColor.getInterpolated(
				MaxStartColor, (CMath::NextRandom(32) % 100) / 100.0f);

			p.startColor = p.color;

			if (SpeedVariation > 0)
			{
				s32 rnd = CMath::NextRandom(32);
				p.vector += ((rnd % SpeedVariation) / 100.0f) * p.vector;
			}

			p.startVector = p.vector;

			Particles.push_back(p);
		}

		outArray = Particles.pointer();

		return Particles.size();
	}

	Time += timeSinceLastCall;
	return 0;
}


s32 CFpsParticleEmitter::emitt2(float now, int nbParticles, SFpsParticle*& outArray)
{
	SFpsParticle p;
	/*const vector3df& extent = Box.getExtent();

	quaternion qRot;
	qRot.rotationFromTo(Vector3D(0,1,0), Direction);

	Vector3D eulerAngles;
	qRot.toEulerDegree(eulerAngles);*/

	Particles.set_used(0);

	for (u32 i=0; i<nbParticles; ++i)
	{
		/*p.pos.X = Box.MinEdge.X + fmodf((f32)CMath::NextRandom(32), extent.X);
		p.pos.Y = Box.MinEdge.Y + fmodf((f32)CMath::NextRandom(32), extent.Y);
		p.pos.Z = Box.MinEdge.Z + fmodf((f32)CMath::NextRandom(32), extent.Z);*/
		p.pos.X = fmodf((f32)CMath::NextRandom(32), m_box.getWidth());
		p.pos.Y = fmodf((f32)CMath::NextRandom(32), m_box.getHeight());		

		p.startTime = now;
		//p.vector = Direction;

		p.width = ParticleWidth;
		p.height = ParticleHeight;

		p.spin = 0;

		int deltaRot = InitialRotMax - InitialRotMin;
		//p.initialRotation = eulerAngles.X + InitialRotMin;

		/*if (deltaRot)
		{
			p.initialRotation = InitialRotMin + (CMath::NextRandom(32) % (deltaRot));
		}*/

		if (SizeVariation != 0)
		{
			int rnd = CMath::NextRandom(32) % (SizeVariation << 1) - SizeVariation;
			p.width = p.width + p.width * rnd / 100;
			p.height = p.height + p.height * rnd / 100;
		}
		
		/*vector3df tgt = Direction;

		if (MaxAngleDegreesXY)
		{
			tgt.rotateXYBy((CMath::NextRandom(32)%(MaxAngleDegreesXY*2)) - MaxAngleDegreesXY);
		}*/


		//p.vector = tgt;

		if (MaxLifeTime - MinLifeTime == 0)
			p.endTime = now + MinLifeTime;
		else
			p.endTime = now + CMath::GetGLRand( MinLifeTime, MaxLifeTime );

		p.color = MinStartColor.getInterpolated(
			MaxStartColor, (CMath::NextRandom(32) % 100) / 100.0f);

		p.startColor = p.color;

		if (SpeedVariation > 0)
		{
			s32 rnd = CMath::NextRandom(32);
			p.vector += ((rnd % SpeedVariation) / 100.0f) * p.vector;
		}

		p.startVector = p.vector;

		Particles.push_back(p);
	}

	outArray = Particles.pointer();

	return Particles.size();
}

void CFpsParticleEmitter::reallocate( int size )
{
	Particles.reallocate( size );
}

void CFpsParticleEmitter::free()
{
	Particles.clear();
}

//! Writes attributes of the object.
void CFpsParticleEmitter::serializeAttributes(IAttributes* out) const
{
	//vector3df b = Box.getExtent();
	//b *= 0.5f;
	//out->addVector3d("Box", b);
	//out->addVector3d("Direction", Direction);
	out->addInt("SpeedVariation", SpeedVariation);
	out->addInt("MinParticlesPerSecond", MinParticlesPerSecond);
	out->addInt("MaxParticlesPerSecond", MaxParticlesPerSecond);
	out->addFloat("ParticleWidth", ParticleWidth);
	out->addFloat("ParticleHeight", ParticleHeight);
	out->addInt("SizeVariation", SizeVariation);
	//out->addColor("MinStartColor", MinStartColor);
	//out->addColor("MaxStartColor", MaxStartColor);
	out->addInt("MinLifeTime", MinLifeTime);
	out->addInt("MaxLifeTime", MaxLifeTime);
	out->addInt("MaxAngleDeg", MaxAngleDegrees);
	out->addInt("InitialRotMin", InitialRotMin);
	out->addInt("InitialRotMax", InitialRotMax);
}


//! Reads attributes of the object.
s32 CFpsParticleEmitter::deserializeAttributes(s32 startIndex, IAttributes* in)
{
	// read data and correct input values here

	float radius = in->getAttributeAsFloat("Radius");
	m_box = rectf(position2df(),dimension2df(radius,radius));
	////if (b.X <= 0)
	////	b.X = 1.0f;
	////if (b.Y <= 0)
	////	b.Y = 1.0f;
	////if (b.Z <= 0)
	////	b.Z = 1.0f;

	//if (b.X < 0)
	//	b.X = 0.0f;
	//if (b.Y < 0)
	//	b.Y = 0.0f;
	//if (b.Z < 0)
	//	b.Z = 0.0f;

	//Box.MinEdge.X = -b.X;
	//Box.MinEdge.Y = -b.Y;
	//Box.MinEdge.Z = -b.Z;
	//Box.MaxEdge.X = b.X;
	//Box.MaxEdge.Y = b.Y;
	//Box.MaxEdge.Z = b.Z;

	//Direction = in->getAttributeAsVector3d("Direction");
	//if (Direction.getLength() == 0)
	//	Direction.set(0,0.01f,0);

	SpeedVariation = in->getAttributeAsInt("SpeedVariation");

	MinParticlesPerSecond = in->getAttributeAsInt("MinParticlesPerSecond");
	MaxParticlesPerSecond = in->getAttributeAsInt("MaxParticlesPerSecond");

	MinParticlesPerSecond = max_(1u, MinParticlesPerSecond);
	MaxParticlesPerSecond = max_(MaxParticlesPerSecond, 1u);
	MaxParticlesPerSecond = min_(MaxParticlesPerSecond, 200u);
	MinParticlesPerSecond = min_(MinParticlesPerSecond, MaxParticlesPerSecond);

	ParticleWidth = in->getAttributeAsFloat("ParticleWidth");
	ParticleHeight = in->getAttributeAsFloat("ParticleHeight");
	SizeVariation = in->getAttributeAsInt("SizeVariation");

	//MinStartColor = in->getAttributeAsColor("MinStartColor");
	//MaxStartColor = in->getAttributeAsColor("MaxStartColor");
	MinLifeTime = in->getAttributeAsInt("MinLifeTime") / 1000.0f;
	if( MinLifeTime < 0 )
		MinLifeTime = Misc_FixedPointMax;
	MaxLifeTime = in->getAttributeAsInt("MaxLifeTime") / 1000.0f;
	if( MaxLifeTime < 0 )
		MaxLifeTime /= Misc_FixedPointMax;
	/*MaxAngleDegrees = in->getAttributeAsInt("MaxAngleDeg");

	InitialRotMin = in->getAttributeAsInt("InitialRotMin");
	InitialRotMax = in->getAttributeAsInt("InitialRotMax");*/

	MinLifeTime = CMath::Max(0, MinLifeTime);
	MaxLifeTime = CMath::Max(MaxLifeTime, MinLifeTime);
	MinLifeTime = CMath::Max(MinLifeTime, MaxLifeTime);

	return in->findAttribute("MaxLifeTime");
}

